'''
生成器是一种特殊的迭代器，它使用函数来产生序列中的元素。
生成器函数使用 yield 关键字来暂停函数执行并产生一个值，然后在需要下一个值时再次恢复执行。
这允许您按需生成值，而不必一次性将所有值加载到内存中。生成器在处理大型数据集时非常高效。
'''

'''

迭代器和生成器在处理序列数据时有许多优点，使得它比普通的 for 循环更加灵活和高效。以下是一些迭代器的优点以及与普通 for 循环的比较：
优点：
1.节省内存：迭代器一次只返回一个元素，而不会一次性将整个序列加载到内存中。这对于大型数据集非常有用，可以有效地减少内存占用。
2.懒惰求值（Lazy Evaluation） ：生成器迭代器使用惰性求值，只在需要时生成值。这意味着您可以在不需要全部数据的情况下开始迭代，从而提高性能和效率。
3.支持无限序列：生成器可以用于表示无限序列，因为它们按需生成值，而不需要在内存中存储整个序列。
4.可复用性和模块化：通过封装生成器逻辑，您可以创建可重用的、模块化的生成器函数，以便在不同的上下文中使用。
缺点：
1.速度相对较慢：与直接使用列表的 for 循环相比，迭代器可能会稍微慢一些，因为它们需要在每次迭代时执行一些附加操作。
2.不适合索引访问：由于迭代器是按需生成值的，所以无法通过索引访问特定位置的元素，需要从头开始迭代。
3.无法修改序列：迭代器一般是只读的，不能用于修改序列中的元素。
适用场景：
1.当您需要处理大型数据集时，迭代器可以节省大量内存，并提高性能。
2.当您需要按需生成值，或者处理无限序列时，生成器是一个非常好的选择。
3.当您需要创建可复用的、模块化的代码时，生成器函数能够提供更好的组织和抽象。
我该怎么选：
使用迭代器的主要优点是节省内存、支持惰性求值和无限序列，同时也提高了代码的可复用性和模块化。然而，对于需要快速索引和修改的情况，使用普通的 for 循环可能更为方便。在选择使用迭代器还是普通循环时，您应该根据具体的情况和需求进行权衡。
'''
# 生成器的创建
def intNum():
    print('开始执行程序：...')
    for i in range(5):
        # 当执行到yield i的时候，会生成i，并且暂停
        # 当下一次调用的时候，会从这里继续运行，到下一个yield再次暂停
        yield i     # return 回到函数调用处，后面的不会执行，yield 可理解为暂停，同时回到函数调用处，但是此时进到for循环
        print('go on')

if __name__ == "__main__":
    # intNum() 函数的返回值用的是 yield 关键字，而不是 return 关键字，此类函数又成为生成器函数。
    num = intNum()   # 生成器函数
    print(num)       # <generator object intNum at 0x104824110>，生成器函数

    # 如果需要继续执行，需要调用 next() 方法，或者用for循环代替
    # print(num.__next__())    # 执行一次，就结束
    # print(num.__next__())
    # print(num.__next__())
    # print(num.__next__())
    # print(num.__next__())    # 如果执行时超出 范围，会报错

    for i in num:
        print(f'for循环：{i}')

    """
    和 return 相比，yield 除了可以返回相应的值，还有一个更重要的功能，即每当程序执行完该语句时，程序就会暂停执行。
    不仅如此，即便调用生成器函数，Python 解释器也不会执行函数中的代码，它只会返回一个生成器（对象）num。
   
    要想使生成器函数得以执行，或者想使执行完 yield 语句立即暂停的程序得以继续执行，有以下 2 种方式：
    1. 通过生成器（上面程序中的 num）调用 next() 内置函数或者 __next__() 方法；
    2. 通过 for 循环遍历生成器。
    """
